#pragma once

#include "Point3D.h"
#include "Triangle3D.h"
#include "GeometryUtils.h"

#include "HSModelAPI.h"
#include "IObjectForPicker.h"
#include <list>
#include <string>
#include <memory>

class KDTree;

class Model3D: public IObjectForPicker
  {
  public:
    class Point3DIteratorConst
      {
      public:
        HSMODEL_API Point3DIteratorConst(const std::list<Point3D*>::const_iterator &i_iter);

        HSMODEL_API Point3DIteratorConst& operator++();    // prefix ++
        HSMODEL_API Point3DIteratorConst operator++(int);  // postfix ++

        HSMODEL_API const TVector3& operator*();
        HSMODEL_API const TVector3* operator->();
        
        HSMODEL_API bool operator==(const Point3DIteratorConst &i_other);
        HSMODEL_API bool operator!=(const Point3DIteratorConst &i_other);

      private:
        std::list<Point3D*>::const_iterator m_iter;
      };

    class Triangle3DIteratorConst
      {
      public:
        HSMODEL_API Triangle3DIteratorConst(const std::list<Triangle3D*>::const_iterator &i_iter);

        HSMODEL_API Triangle3DIteratorConst& operator++();    // prefix ++
        HSMODEL_API Triangle3DIteratorConst operator++(int);  // postfix ++

        HSMODEL_API const Triangle3D& operator*() const;
        HSMODEL_API const Triangle3D* operator->() const;
        
        HSMODEL_API bool operator==(const Triangle3DIteratorConst &i_other);
        HSMODEL_API bool operator!=(const Triangle3DIteratorConst &i_other);

      private:
        std::list<Triangle3D*>::const_iterator m_iter;
      };

    class Triangle3DIterator
      {
      public:
        HSMODEL_API Triangle3DIterator(const std::list<Triangle3D*>::iterator &i_iter);

        HSMODEL_API Triangle3DIterator& operator++();    // prefix ++
        HSMODEL_API Triangle3DIterator operator++(int);  // postfix ++

        HSMODEL_API Triangle3D& operator*() const;
        HSMODEL_API Triangle3D* operator->() const;

        HSMODEL_API bool operator==(const Triangle3DIterator &i_other);
        HSMODEL_API bool operator!=(const Triangle3DIterator &i_other);
      private:
        std::list<Triangle3D*>::iterator m_iter;
      };

    class PointsIteratorGetter
      {
      public:
        HSMODEL_API PointsIteratorGetter(const std::list<Point3D*> &i_list);
        HSMODEL_API Point3DIteratorConst begin();
        HSMODEL_API Point3DIteratorConst end();

      private:
        const std::list<Point3D*> &m_list;
      };

    class TrianglesIteratorConstGetter
      {
      public:
        HSMODEL_API TrianglesIteratorConstGetter(const std::list<Triangle3D*> &i_list);
        HSMODEL_API Triangle3DIteratorConst begin();
        HSMODEL_API Triangle3DIteratorConst end();

      private:
        const std::list<Triangle3D*> &m_list;
      };

    class TrianglesIteratorGetter
      {
      public:
        HSMODEL_API TrianglesIteratorGetter(std::list<Triangle3D*> &i_list);
        HSMODEL_API Triangle3DIterator begin();
        HSMODEL_API Triangle3DIterator end();
      private:
        std::list<Triangle3D*> &m_list;
      };

  public:
    HSMODEL_API Model3D();
    HSMODEL_API Model3D(std::string i_material_name);
    HSMODEL_API ~Model3D();

    HSMODEL_API void Clear();
    HSMODEL_API Point3DID     AddPoint(double i_x, double i_y, double i_z);
    HSMODEL_API Point3DID     AddPoint(TVector3 i_point);
    HSMODEL_API Triangle3DID  AddTriangle(const Point3DID &i_first, const Point3DID &i_second, const Point3DID &i_third);

    HSMODEL_API PointsIteratorGetter    Points() const;
    HSMODEL_API TrianglesIteratorConstGetter Triangles() const;
    HSMODEL_API TrianglesIteratorGetter TrianglesMutable();

    HSMODEL_API std::string GetMaterialName() const;
    HSMODEL_API void SetMaterialName(std::string i_material_name);

    HSMODEL_API virtual std::vector<TTriangle3> PickTriangles(const TLine3& i_line) const override;

    HSMODEL_API void ScaleTransform(double i_coef);
    HSMODEL_API void ScaleTransform(const TVector3& i_coef);
    HSMODEL_API void ParalelShift(const TVector3& i_coef);
    
    HSMODEL_API std::vector<const Triangle3D*> GetNeighborTriangles(Point3DID i_id) const;
    HSMODEL_API bool ContainsTriangle(const TTriangle3& i_triangle) const;

    HSMODEL_API std::shared_ptr<Model3D> Clone() const;

  private:
    Model3D(const Model3D &i_other) {};               // Forbidden
    Model3D& operator=(const Model3D &i_other) {};    // Forbidden

  private:
    std::list<Point3D*> m_points;
    std::list<Triangle3D*> m_triangles;

    std::string m_material_name;

    mutable KDTree* mp_kdtree;
  };